////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    WitchesAndWizardsGuts.cpp                                    //
//  Developer:																  //	
//  Create date:                                                              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Witches And Wizards.          //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        2010 - Riplife Gaming.  All rights reserved.						  //
////////////////////////////////////////////////////////////////////////////////

#include "WitchesAndWizardsGuts.h"
#include "slotconn.h"
#include "sltsql.h"

////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Pears, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1			2					3						4						5
/* ICON_CAULDRON */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_CAULDRON,		EVT_FOUR_CAULDRON,		EVT_FIVE_CAULDRON	},
/* ICON_PIC1 */			{	EVT_LOSER,	EVT_LOSER,	EVT_TWO_PIC1,		EVT_THREE_PIC1,			EVT_FOUR_PIC1,			EVT_FIVE_PIC1		},
/* ICON_PIC2 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_PIC2,			EVT_FOUR_PIC2,			EVT_FIVE_PIC2		},
/* ICON_PIC3 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_PIC3,			EVT_FOUR_PIC3,			EVT_FIVE_PIC3		},
/* ICON_PIC4 */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_PIC4,			EVT_FOUR_PIC4,			EVT_FIVE_PIC4		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_ACE,			EVT_FOUR_ACE,			EVT_FIVE_ACE		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_KING,			EVT_FOUR_KING,			EVT_FIVE_KING		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_QUEEN,		EVT_FOUR_QUEEN,			EVT_FIVE_QUEEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_JACK,			EVT_FOUR_JACK,			EVT_FIVE_JACK		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_TEN,			EVT_FOUR_TEN,			EVT_FIVE_TEN		},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_THREE_NINE,			EVT_FOUR_NINE,			EVT_FIVE_NINE		},
/* ICON_WAND */			{	EVT_LOSER,	EVT_LOSER,	EVT_LOSER,			EVT_LOSER,				EVT_LOSER,				EVT_LOSER			}
};


////////////////////////////////////////////////////////////////////////////
/// FruitFrenzyGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Scatter, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_SCATTER ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_SCATTER.
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::m_aScatterWinSpinResults[][6] = 
{
// Number of icons:			0			1						2					3					4					5
/* ICON_CAULDRON */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC1 */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC2 */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC3 */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_PIC4 */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,				EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_WAND */			{	EVT_LOSER,	EVT_SCATTER_NEAR_MISS,	EVT_TWO_WAND,		EVT_THREE_WAND,		EVT_FOUR_WAND,		EVT_FIVE_WAND		}
};


////////////////////////////////////////////////////////////////////////////
/// WitchesAndWizardsGuts::m_aPayLineOffsets
///
/// Declare an array to look up the offset for a reel for each payline.
/// An offset of 0 means that the reel should stop on the middle row,
/// an offset of 1 means the reel should stop on the top row,
/// and an offset of -1 means the reel should stop on the bottom row.
/// So the offset is relative to the middle row, or pay line 1.
////////////////////////////////////////////////////////////////////////////
int8 WitchesAndWizardsGuts::m_aPayLineOffsets[25][5] = 
{
	{  0,  0,  0,  0,  0 },	// Pay line 1
	{  1,  1,  1,  1,  1 },	// Pay line 2
	{ -1, -1, -1, -1, -1 },	// Pay line 3
	{  1,  0, -1,  0,  1 },	// Pay line 4
	{ -1,  0,  1,  0, -1 },	// Pay line 5
	{  1,  1,  0, -1, -1 },	// Pay line 6
	{ -1, -1,  0,  1,  1 },	// Pay line 7
	{  0,  1,  0, -1,  0 },	// Pay line 8
	{  0, -1,  0,  1,  0 },	// Pay line 9
	{  1,  0,  0,  0, -1 },	// Pay line 10
	{ -1,  0,  0,  0,  1 },	// Pay line 11
	{  0,  1,  1,  0, -1 },	// Pay line 12
	{  0, -1, -1,  0,  1 },	// Pay line 13
	{  0,  0,  1,  0, -1 },	// Pay line 14
	{  0,  0, -1,  0,  1 },	// Pay line 15
	{  1,  1,  0, -1,  0 },	// Pay line 16
	{ -1, -1,  0,  1,  0 },	// Pay line 17
	{  0,  1,  0, -1, -1 },	// Pay line 18
	{  0, -1,  0,  1,  1 },	// Pay line 19
	{  1,  1,  1,  0, -1 },	// Pay line 20
	{ -1, -1, -1,  0,  1 },	// Pay line 21
	{  1,  0, -1, -1, -1 },	// Pay line 22
	{ -1,  0,  1,  1,  1 },	// Pay line 23
	{  1,  0,  1,  0,  1 },	// Pay line 24
	{ -1,  0, -1,  0, -1 }	// Pay line 25
};

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::WitchesAndWizardsGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::WitchesAndWizardsGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );

	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aScatterWinSpinResults array. 
	C_ASSERT( countof( m_aScatterWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::~WitchesAndWizardsGuts()
///
/// Destructor for WitchesAndWizardsGuts. 
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::~WitchesAndWizardsGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::getCauldronLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Cauldron line win payout.
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::getCauldronLineWinResult( uint8* stops, uint8 line, uint8 &multiplier )
{
	// Count how many Cauldrons we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Cauldron...
		int32 iIconID = getIcon( getGameModeId(), i, stops[i] )->iconid;
		if ( iIconID != ICON_CAULDRON )
		{
			// If we are in a free spin round, maybe that icon was substituted by a cauldron
			if ( getGameModeId() == MODE_FREE_SPIN )
			{
				int32 iBonusGameType = m_subGame->getData( SGDIDX_SUBGAME_ID );
				if (iBonusGameType == FREE_SPIN_WILD_EXPLOSION || iBonusGameType == FREE_SPIN_ADDED_WILD)
				{
					int32 iBonusGameData = m_subGame->getData( SGDIDX_SUBGAME_DATA );
					// In this case, data is a mask with the slot position becoming Wild
					//uint8 row = SlotGuts::getPaylinePosition(line, i);
					uint8 row = getRowLineReel(line, i);

					int32 position = 1 << (row * 5 + i);
					// If that position is not a Cauldron substitution, we have finished
					if ((iBonusGameData & position) == 0)
					{
						break;
					}
				}
				else if (iBonusGameType == FREE_SPIN_WILD_EXPAND)
				{
					int32 iBonusGameData = m_subGame->getData( SGDIDX_SUBGAME_DATA );
					// In this case, data is a mask with the column numbers expanded to Wild
					
					int32 position = 1 << i;
					// If that position is not a Cauldron substitution, we have finished
					if ((iBonusGameData & position) == 0)
					{
						break;
					}
				}
				else
				{
					break;
				}
			}			
			else
			{
				break;
			}
		}
    }

	// Use the array to look up the spin result for i Cauldrons.
	return lookupLineWinSpinResult( ICON_CAULDRON, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 line, uint8 testIcon, bool& bSubstitution, uint8 &multiplier )
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Cauldron (Wild)...
		if ( currentIcon == ICON_CAULDRON )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		// If the current icon is substitued by a Cauldron (Wild)...
		else
		{
			if ( getGameModeId() == MODE_FREE_SPIN )
			{
				int32 iBonusGameType = m_subGame->getData( SGDIDX_SUBGAME_ID );
				if (iBonusGameType == FREE_SPIN_WILD_EXPLOSION || iBonusGameType == FREE_SPIN_ADDED_WILD)
				{
					int32 iBonusGameData = m_subGame->getData( SGDIDX_SUBGAME_DATA );
					// In this case, data is a mask with the slot position becoming Wild
					//uint8 row = SlotGuts::getPaylinePosition(line, i);
					uint8 row = getRowLineReel(line, i);

					int32 position = 1 << (row*5 + i);
					if ((iBonusGameData & position) != 0)
					{
						bSubstitution = true;
						continue;						
					}
				}
				else if (iBonusGameType == FREE_SPIN_WILD_EXPAND)
				{
					int32 iBonusGameData = m_subGame->getData( SGDIDX_SUBGAME_DATA );
					// In this case, data is a mask with the column numbers expanded to Wild
					int32 position = 1 << i;
					if ((iBonusGameData & position) != 0)
					{						
						bSubstitution = true;
						continue;						
					}
				}
			}			
		}
		
		// Otherwise if the test icon is a Cauldron...
		if ( testIcon == ICON_CAULDRON )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Cauldron.
			bSubstitution = true;

			// We have now found our first icon that is not a Cauldron.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			break;											
        }
    }

	// If we made it all the way through and the testIcon is still a cauldron...
	if ( testIcon == ICON_CAULDRON )
	{
		// then this must be a 5 cauldron win, so reset the substitution flag.
		bSubstitution = false;
	}

	// Use the array to look up the spin result for i testIcons.
  	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool WitchesAndWizardsGuts::doesCauldronLineWinPayMore( SpinResult srCauldron, SpinResult srOther ) const
///
/// @param   srCauldron		The spin result to calculate the Cauldron payout for.
/// @param   srOther		The spin result to calculate the other payout for.
///
/// @return	 bool			True if the Cauldron win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool WitchesAndWizardsGuts::doesCauldronLineWinPayMore( SpinResult srCauldron, uint8 multiplierCauld,
	SpinResult srOther, uint8 multiplierSubs ) const
{
	// Lookup the payout for the Cauldron line win.
	long nCauldronLineWinPayout = getPayout( srCauldron )->payoutAmount * multiplierCauld;

	// Lookup the payout for the other line win.
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount * multiplierSubs;

	// Return true if the Cauldron payout is bigger.
    return ( nCauldronLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	uint8 multiplierCauld = 1;
	uint8 multiplierSubs = 1;

	// Track the Cauldron result separately since it might be changed to a substitution.
	SpinResult srCauldronLineWinResult = getCauldronLineWinResult( stops, line, multiplierCauld );  

	// Now see what the payout is if we use any Cauldron we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// Maybe the first line position was substitued by a Wild
	if ( getGameModeId() == MODE_FREE_SPIN )
	{
		int32 iBonusGameType = m_subGame->getData( SGDIDX_SUBGAME_ID );
		if (iBonusGameType == FREE_SPIN_WILD_EXPLOSION || iBonusGameType == FREE_SPIN_ADDED_WILD)
		{
			int32 iBonusGameData = m_subGame->getData( SGDIDX_SUBGAME_DATA );
			// In this case, data is a mask with the slot position becoming Wild
			//uint8 row = SlotGuts::getPaylinePosition(line, i);
			uint8 row = getRowLineReel(line, 0);

			int32 position = 1 << (5 * row);
			// If that position is a Cauldron substitution, we have a substitution. Modify test icon
			if ((iBonusGameData & position) > 0)
			{
				bSubstitution = true;
				testIcon = ICON_CAULDRON;
			}
		}
		else if (iBonusGameType == FREE_SPIN_WILD_EXPAND)
		{
			int32 iBonusGameData = m_subGame->getData( SGDIDX_SUBGAME_DATA );
			// In this case, data is a mask with the column numbers expanded to Wild
			
			int32 position = 1; // First column
			// If that position is a Cauldron substitution, we have a substitution. Modify test icon
			if ((iBonusGameData & position) > 0)
			{
				bSubstitution = true;
				testIcon = ICON_CAULDRON;
			}
		}
	}

	// See what the spin result is if we used any Cauldron as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, line, testIcon, bSubstitution, multiplierSubs );

	// If cauldron substitution, multiply by two
	if (bSubstitution)
		multiplierSubs = multiplierSubs * MULTIPLIER_CAULDRON_SUBS;

	// Use the largest payout between the regular Cauldron evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesCauldronLineWinPayMore( srCauldronLineWinResult, multiplierCauld, 
		srResultWithPossibleSubstitution, multiplierSubs ) )
	{
		// The Cauldrons by themselves pay out more so use the Cauldron result.
		rtnresult = srCauldronLineWinResult;

		multiplier = multiplierCauld;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note.
			spinline->addNote( NOTE_CAULDRON_SUBSTITUTION );
		}

		multiplier = multiplierSubs;
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		WitchesAndWizardsGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( spin->getSpinLineCount() ) ||
			m_bForceRandomJackpot)
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }

	// In third party games (API) we include the jackpot in every spin so that they can update it.
	// If no jackpot, they have not won the jackpot
	setJackpotAmount( getProgressiveValue() );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::getScatterWinResult()   
///
/// @param   spin				The current spin object.
/// @param   testIcon			The scatter icon that we are searching for.
/// @param   scatterPositions	The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
WitchesAndWizardsGuts::SpinResult WitchesAndWizardsGuts::getScatterWinResult( SlotSpin* spin, uint8 testIcon, int32& scatterPositions ) const
{
	// Declare a local variable to keep up with the icon count.
	uint8 numScatterIcons = 0;

	// Initialize the scatterPositions.
	scatterPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == testIcon )
			{
				// then increase the icon count
				numScatterIcons++;

				// Set the bit in the position mask for this stop.
				scatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for numScatterIcons.
	return lookupScatterWinSpinResult( testIcon, numScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 scatterPositions )
{
	// If we got the scatter win (or almost got it)
	if ( srScatterResult != EVT_LOSER )
	{
		// Initialize our multiplier for scatters.
		int32 multiplier = 1;
		int8 noteId = -1;

		// If we are in free spin mode...		
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// If the free spin type is a Random Multiplier, the multiplier is stored in the subgame data
			int32 iBonusGameType = m_subGame->getData( SGDIDX_SUBGAME_ID );
			if (iBonusGameType == FREE_SPIN_RANDOM_MULTIPLIER)
			{
				multiplier = m_subGame->getData( SGDIDX_SUBGAME_DATA );
				noteId = NOTE_RANDOM_MULTIPLIER;
			}
			else if (iBonusGameType == FREE_SPIN_WILD_EXPAND)
			{
				multiplier = MULTIPLIER_WILD_EXPAND; 
				noteId = NOTE_DOUBLE_WILD_EXPAND;
			}
		}

		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= spin->getSpinLineCount();

		// Add the payout (with multiplier and bet per line) to the total win for this spin.
		increaseTotalWin( nPayout * multiplier * getBetPerLine() );
    
		// Add a scatter to the spin.
		int nFreeSpinsAwarded = srScatterResult >= EVT_THREE_WAND ? 10 : 0; 
		SlotScatter* pScatter = spin->addScatter( scatterPositions,
			nPayout * getBetPerLine(), multiplier, false, srScatterResult, nFreeSpinsAwarded );

		// If the free spin multiplier was used...
		if ( noteId != -1 )
		{
			// then add the free spin note.
			pScatter->addNote( noteId );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare a local variable to track the scatter positions.
	int32 nScatterPositions = 0;

	// See if there is a Wand scatter.
	SpinResult srResult = getScatterWinResult( spin, ICON_WAND, nScatterPositions );

	// Add the scatter for Wand, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	//  If three or more scatters, activate free spins
	if (srResult == EVT_THREE_WAND || srResult == EVT_FOUR_WAND || srResult == EVT_FIVE_WAND)
	{
		if (getFreeSpins() == 0)
		{
			m_subGame->update( SGDIDX_SUBGAME_ID, 0 );  // Start Free Spins
			m_subGame->update( SGDIDX_SUBGAME_DATA, 0 );  			
		}
		incrementFreeSpins( FREE_SPIN_ROUNDS );
		setGameModeId( MODE_FREE_SPIN );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 WitchesAndWizardsGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range. GENESIS GAMES: [9000,9500)
    return ( getGutsId() >= 9000 && getGutsId() < 9500 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		// If the free spin type is a Random Multiplier, the multiplier is stored in the subgame data
		int32 iBonusGameType = m_subGame->getData( SGDIDX_SUBGAME_ID );
		if (iBonusGameType == FREE_SPIN_RANDOM_MULTIPLIER)
		{
			nGameModeMultiplier = m_subGame->getData( SGDIDX_SUBGAME_DATA );
			nNoteId = NOTE_RANDOM_MULTIPLIER;
		}
		else if (iBonusGameType == FREE_SPIN_WILD_EXPAND)
		{
			nGameModeMultiplier = MULTIPLIER_WILD_EXPAND; 
			nNoteId = NOTE_DOUBLE_WILD_EXPAND;
		}
		else 
		{
			// just defer to the base class.
			SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
		}
		
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::preSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden when running in debug spins mode to alter the reel stops.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::preSpinClassifyResults( SlotSpin* spin )
{
	g_nFreeSpinType = -1;
	
	SlotGuts::preSpinClassifyResults(spin);

	// Free games? Can be a multiplier or adding/expanding wilds
	m_subGame->update( SGDIDX_SUBGAME_DATA, 0 );
	if ( getGameModeId() == MODE_FREE_SPIN )
	{
		// We have to decide the free spin type, according to a probability table
		int8 iFreeSpinType;
		if (m_bDebugSpinsMode && (g_nFreeSpinType > 0))
		{
			iFreeSpinType = g_nFreeSpinType;
		}
		else
		{
			iFreeSpinType = getRandomValueFromProbabilityTable( PT_FREE_GAME_TYPE );
		}
		m_subGame->update( SGDIDX_SUBGAME_ID, iFreeSpinType );
		if (iFreeSpinType > NO_FREE_SPIN)
		{
			switch(iFreeSpinType)
			{
				case FREE_SPIN_WILD_EXPLOSION:		evaluateWildExplosion(spin);	
													break;
				case FREE_SPIN_ADDED_WILD:			evaluateAddedWilds(spin);
													break;
				case FREE_SPIN_WILD_EXPAND:			evaluateWildExpand(spin);
													break;
				case FREE_SPIN_RANDOM_MULTIPLIER:	evaluateRandomMultiplier();
													break;
			}
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::postSpinClassifyResults( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This method is overridden to see if we can complete the game.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::postSpinClassifyResults( SlotSpin* spin )
{
	// If we have not reached the feature cap yet and we have feature rounds...
	// NO FEATURE BONUS IN WITCHES AND WIZARDS
	/*
	if ( !getMaxPayoutAwarded() && getNumFeatureRounds() ) 
	{
		// then get ready to play the first feature round.
		initializeTotalPointsForRound();
		initializeCurrentShotsRemaining();
		initializeCurrentPoints();

		// The game can't complete until the bonus game is done.
		setGameCanComplete( false );
		setBonusGameState( BONUS_GAME_ACTIVE );
	}
	*/
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 WitchesAndWizardsGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}


/*
DEBUG FUNCTIONS
*/

int WitchesAndWizardsGuts::g_nFreeSpinType = -1;


void WitchesAndWizardsGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WILD_EXPLOSION, 
		new DebugStopFunc<WitchesAndWizardsGuts>(this, &WitchesAndWizardsGuts::DebugFreeSpin_WildExplosion) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_WILD_GROTH, 
		new DebugStopFunc<WitchesAndWizardsGuts>(this, &WitchesAndWizardsGuts::DebugFreeSpin_WildGroth) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_METAMORPHA_WILD, 
		new DebugStopFunc<WitchesAndWizardsGuts>(this, &WitchesAndWizardsGuts::DebugFreeSpin_MetamorphaWild) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_MULTIPLIER, 
		new DebugStopFunc<WitchesAndWizardsGuts>(this, &WitchesAndWizardsGuts::DebugFreeSpin_Multiplier) );
}

void WitchesAndWizardsGuts::DebugFreeSpin_WildExplosion( SlotSpin *spin )
{
	g_nFreeSpinType = FREE_SPIN_WILD_EXPLOSION;
}

void WitchesAndWizardsGuts::DebugFreeSpin_WildGroth( SlotSpin *spin )
{
	g_nFreeSpinType = FREE_SPIN_WILD_EXPAND;
}

void WitchesAndWizardsGuts::DebugFreeSpin_MetamorphaWild( SlotSpin *spin )
{
	g_nFreeSpinType = FREE_SPIN_ADDED_WILD;
}

void WitchesAndWizardsGuts::DebugFreeSpin_Multiplier( SlotSpin *spin )
{
	g_nFreeSpinType = FREE_SPIN_RANDOM_MULTIPLIER;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::evaluateWildExplosion(uint8* stops)
///
/// @param   stops   Spin result
/// This function only modifies the supplied value if the value of g_nFeatureWins
/// is > -1.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::evaluateWildExplosion(SlotSpin* spin)
{
	int32 iNewWilds = 0;

	// We have to look for the icon becoming wild, according to a probability table
	int iTestIcon = getRandomValueFromProbabilityTable( PT_WILD_EXPLOSION );

	// We go through the spin result, looking for iIconID symbols to become wilds
	for(int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for
			if ( currentIcon == iTestIcon )
			{
				// Set the bit in the position mask for this stop.
				iNewWilds |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	m_subGame->update(SGDIDX_SUBGAME_DATA, iNewWilds);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::evaluateAddedWilds(uint8* stops)
///
/// @param   stops   Spin result
/// This function only modifies the supplied value if the value of g_nFeatureWins
/// is > -1.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::evaluateAddedWilds(SlotSpin* spin)
{
	int32 iNewWilds = 0;

	// We go through the spin result, calculating wether an icon becomes Wild or Not
	for(int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// The probability table is different for last reel
		uint8 pTable = reel < getReelCount()-1 ? PT_ADDED_WILDS_COLS_1_4 : PT_ADDED_WILDS_COL_5;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Does it become Wild?
			int iBecomesWild = getRandomValueFromProbabilityTable( pTable );

			if (iBecomesWild)
			{
				// Set the bit in the position mask for this stop.
				iNewWilds |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	m_subGame->update(SGDIDX_SUBGAME_DATA, iNewWilds);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::evaluateWildExpand(uint8* stops)
///
/// @param   stops   Spin result
/// This function only modifies the supplied value if the value of g_nFeatureWins
/// is > -1.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::evaluateWildExpand(SlotSpin* spin)
{
	int32 columnsExpanded = 0;

	uint8 gameMode = getGameModeId();

	// No probability here. All wilds expands in their column
	for(int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// We get the stops for this reel
		uint8 reelStops = spin->getStops()[ reel ];

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( gameMode, reel, reelStops, row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( gameMode, reel, stop )->iconid;

			// If it is a Wild, expand it to his column
			if (currentIcon == ICON_CAULDRON)
			{
				// Set column expanded (it is what we are going to send to the client)
				columnsExpanded |= 1 << reel;

				// Go to next column
				break;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	m_subGame->update(SGDIDX_SUBGAME_DATA, columnsExpanded);
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::evaluateRandomMultiplier()
///
/// This function only modifies the supplied value if the value of g_nFeatureWins
/// is > -1.
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::evaluateRandomMultiplier()
{
	// We get the random multiplier from a probability table
	int iMultiplier = getRandomValueFromProbabilityTable( PT_RANDOM_MULTIPLIER );

	m_subGame->update(SGDIDX_SUBGAME_DATA, iMultiplier);	
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 WitchesAndWizardsGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void WitchesAndWizardsGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=32;
	pStops[1]=14;
	pStops[2]=14;
	pStops[3]=28;
	pStops[4]=44;
}

int32 WitchesAndWizardsGuts::getProgressiveValue()
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;
   int32 jackpot = 0;

    if ( tracker )
    {
    	slotTracker->lock();
		slotTracker->getJackpot(getJackpotId(), jackpot, getInfo()->forMoney, 1);
    	slotTracker->unlock();
    }

	return jackpot;
}

